package com.zmm.weblog.web.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.zmm.weblog.admin.event.ReadArticleEvent;
import com.zmm.weblog.common.domain.dos.*;
import com.zmm.weblog.common.domain.mapper.*;
import com.zmm.weblog.common.enums.ResponseCodeEnum;
import com.zmm.weblog.common.exception.BizException;
import com.zmm.weblog.common.utils.PageResponse;
import com.zmm.weblog.common.utils.Response;
import com.zmm.weblog.web.convert.ArticleConvert;
import com.zmm.weblog.web.markdown.MarkdownHelper;
import com.zmm.weblog.web.module.vo.article.*;
import com.zmm.weblog.web.module.vo.category.FindCategoryListRspVo;
import com.zmm.weblog.web.module.vo.tag.FindTagListRspVo;
import com.zmm.weblog.web.service.ArticleService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: 福禄寿
 * @Date: 2024/7/8 下午4:55
 * @Des: weblog-springboot
 **/
@Service
public class ArticleServiceImpl implements ArticleService {

    private static final Logger log = LoggerFactory.getLogger(ArticleServiceImpl.class);
    @Autowired
    ArticleMapper articleMapper;

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private ArticleCategoryRelMapper articleCategoryRelMapper;

    @Autowired
    private ArticleTagRelMapper articleTagRelMapper;

    @Autowired
    private TagMapper tagMapper;
    @Autowired
    private ArticleContentMapper articleContentMapper;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    /**
     * 获取首页文章分页数据
     *
     * @param findIndexArticlePageListReqVo
     * @return
     */
    @Override
    public Response findArticlePageList(FindIndexArticlePageListReqVo findIndexArticlePageListReqVo) {

        Long current = findIndexArticlePageListReqVo.getCurrent();
        Long size = findIndexArticlePageListReqVo.getSize();

        //第一步：分页查询文章主体记录
        Page<ArticleDo> articleDOPage = articleMapper.selectPageList(current, size, null, null, null);

        //返回的分页数据
        List<ArticleDo> articleDos = articleDOPage.getRecords();

        List<FindIndexArticlePageListRspVo> vos = null;
        if (!CollectionUtils.isEmpty(articleDos)) {
            //文章DO转VO
            vos = articleDos.stream()
                    .map(ArticleConvert.INSTANCE::convertDO2VO)
                    .collect(Collectors.toList());
            //拿到所有文章的ID集合
            List<Long> articleIds = articleDos.stream().map(ArticleDo::getId).collect(Collectors.toList());
            //第二步：设置文章所属分类
            //查询所有分类
            List<CategoryDo> categoryDos = categoryMapper.selectList(null);
            //转Map,方便后续根据分类ID拿到对应的分类名称
            Map<Long, String> categoryDoMap = categoryDos.stream().collect(Collectors.toMap(CategoryDo::getId, CategoryDo::getName));
            //根据文章ID批量查询所有关联记录
            List<ArticleCategoryRelDo> articleCategoryRelList = articleCategoryRelMapper.selectBatchByArticleIds(articleIds);

            vos.forEach(vo -> {
                Long id = vo.getId();
                //过滤出当前文章的标签关联记录
                Optional<ArticleCategoryRelDo> optional = articleCategoryRelList.stream()
                        .filter(articleCategoryRelDO -> articleCategoryRelDO.getArticleId().equals(id)).findAny();
                if (optional.isPresent()) {
                    ArticleCategoryRelDo articleCategoryRelDO = optional.get();
                    Long categoryId = articleCategoryRelDO.getCategoryId();
                    //通过分类Id从map中获取相应的值
                    String categoryName = categoryDoMap.get(categoryId);
                    FindCategoryListRspVo findCategoryListRspVo = FindCategoryListRspVo.builder()
                            .id(categoryId)
                            .name(categoryName)
                            .build();

                    vo.setCategory(findCategoryListRspVo);
                }
            });

            //第三步：设置文章所属标签
            //查询所有标签
            List<TagDo> tagDos = tagMapper.selectList(null);
            //转Map，方便后期提取
            Map<Long, String> tagDoMap = tagDos.stream().collect(Collectors.toMap(TagDo::getId, TagDo::getName));
            //根据文章ID批量查询所有关联记录
            List<ArticleTagRelDo> articleTagRelList = articleTagRelMapper.selectBatchByArticleIds(articleIds);

            vos.forEach(vo -> {
                Long id = vo.getId();
                //过滤出当前文章的标签关联记录
                List<ArticleTagRelDo> articleTagRelDoList = articleTagRelList.stream().filter(articleTagRelDO -> articleTagRelDO.getArticleId().equals(id))
                        .collect(Collectors.toList());
                List<FindTagListRspVo> findTagPageListRspVos = Lists.newArrayList();
                articleTagRelDoList.forEach(articleTagRelDO -> {
                    Long tagId = articleTagRelDO.getTagId();
                    //通过分类Id从map中获取相应的值
                    FindTagListRspVo findTagListRspVo = FindTagListRspVo.builder()
                            .id(tagId)
                            .name(tagDoMap.get(tagId))
                            .build();
                     findTagPageListRspVos.add(findTagListRspVo);
                });
                vo.setTags(findTagPageListRspVos);
            });

        }

        return PageResponse.success(articleDOPage,vos);
    }

    /**
     * 获取文章详情
     * @param findArticleDetailReqVo
     * @return
     */
    @Override
    public Response findArticleDetail(FindArticleDetailReqVo findArticleDetailReqVo) {
        Long articleId = findArticleDetailReqVo.getArticleId();
        //获取文章
        ArticleDo articleDo = articleMapper.selectById(articleId);
        //查询文章是否存在
        if (articleDo == null){
            log.warn("文章不存在，articleId:{}",articleId);
            throw new BizException(ResponseCodeEnum.ARTICLE_NOT_FOUND);
        }
        //查询正文
        ArticleContentDo articleContentDo = articleContentMapper.selectByArticleId(articleId);
        if (articleContentDo == null){
            log.warn("文章正文不存在，articleId:{}",articleId);
            throw new BizException(ResponseCodeEnum.ARTICLE_NOT_FOUND);
        }
        //DO转VO
        FindArticleDetailRspVo vo = FindArticleDetailRspVo.builder()
                .title(articleDo.getTitle())
                .createTime(articleDo.getCreateTime())
                .content(MarkdownHelper.convertMarkdown2Html(articleContentDo.getContent()))
                .readNum(articleDo.getReadNum())
                .build();
        //查询分类
        ArticleCategoryRelDo articleCategoryRelDo = articleCategoryRelMapper.selectByArticleId(articleId);
        CategoryDo categoryDo = categoryMapper.selectById(articleCategoryRelDo.getCategoryId());
        vo.setCategoryName(categoryDo.getName());
        vo.setCategoryId(categoryDo.getId());

        //查询标签
        List<ArticleTagRelDo> articleTagRelDos = articleTagRelMapper.selectByArticleId(articleId);
        List<Long> tagIds = articleTagRelDos.stream().map(ArticleTagRelDo::getTagId).collect(Collectors.toList());
        List<TagDo> tagDos = tagMapper.selectBatchIds(tagIds);

        List<FindTagListRspVo> tagVos = tagDos.stream().map(tagDo ->
                FindTagListRspVo.builder().id(tagDo.getId()).name(tagDo.getName()).build()).collect(Collectors.toList());
        vo.setTags(tagVos);

        ArticleDo preArticleDo = articleMapper.selectPreArticle(articleId);
        if (preArticleDo != null){
            FindPreNextArticleRspVo preArticle = FindPreNextArticleRspVo.builder()
                    .articleId(preArticleDo.getId())
                    .articleTitle(preArticleDo.getTitle()).build();
            vo.setPreArticle(preArticle);
        }

        ArticleDo nextArticleDo = articleMapper.selectNextArticle(articleId);
        if (nextArticleDo != null){
            FindPreNextArticleRspVo nextArticle = FindPreNextArticleRspVo.builder()
                    .articleId(nextArticleDo.getId())
                    .articleTitle(nextArticleDo.getTitle()).build();
            vo.setNextArticle(nextArticle);
        }

        applicationEventPublisher.publishEvent(new ReadArticleEvent(this, articleId));

        return Response.success(vo);
    }
}
